﻿using MyX.Core;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace MyX.Message
{
    /// <summary>
    /// 消息基类
    /// </summary>
    public abstract class XMessage : XIMessage
    {
        /// <summary>
        /// 初始化消息ID，时间戳
        /// </summary>
        public XMessage()
        {
            ID = Guid.NewGuid();
            TimeStamp = DateTime.Now;
        }

        internal void Check()
        {
            var type = this.GetType();
            var fields = type.GetFields();
            var properties = type.GetProperties();

            if (MemberChecker.HasAllowNull(type))
            {
                fields.ToList().ForEach(x => MemberChecker.ClassNullCheck(this, x));
                properties.ToList().ForEach(x => MemberChecker.ClassNullCheck(this, x));
            }
            else
            {
                fields.ToList().ForEach(x => MemberChecker.NullCheck(this, x));
                properties.ToList().ForEach(x => MemberChecker.NullCheck(this, x));
            }
        }

        /// <summary>
        /// 消息ID
        /// </summary>
        public Guid ID { get; set; }

        /// <summary>
        /// 时间戳
        /// </summary>
        public DateTime TimeStamp { get; set; }
    }

    internal class MemberChecker
    {

        public static void ClassNullCheck(object instance, MemberInfo member)
        {
            if (HasVaild(member))
            {
                var value = GetValue(instance, member);
                if (value == null)
                    return;
                var vaild = GetVaild(member);
                if (!vaild.IsVaild(value))
                    ThrowVaild(member);
            }
        }

        public static void NullCheck(object instance, MemberInfo member)
        {
            var value = GetValue(instance, member);
            if (HasAllowNull(member))
                return;
            if (value == null)
                ThrowNull(member);
            if (ValueIsNull(value))
                ThrowNull(member);
            if (HasVaild(member))
            {
                var vaild = GetVaild(member);
                if (!vaild.IsVaild(value))
                    ThrowVaild(member);
            }
        }

        private static bool ValueIsNull(object value)
        {
            if (value == null)
                return true;

            //当是值类型时，除guid，和datetime，进行值判断，其余默认返回都是不为空
            if (value is ValueType)
            {
                if (value.GetType() == typeof(Guid))
                    return (Guid)value == Guid.Empty;
                if (value.GetType() == typeof(DateTime))
                    return (DateTime)value == new DateTime();

                return false;
            }
            //如果是引用类型，则判断是否为集合
            var collection = value as ICollection;
            if (collection != null)
                return collection.Count == 0;

            return false;
        }

        public static bool HasAllowNull(MemberInfo member)
        {
            return member.GetCustomAttribute(typeof(XAllowNullAttribute), false) != null;
        }

        public static bool HasVaild(MemberInfo member)
        {
            return member.GetCustomAttribute(typeof(XVaildValueAttribute), false) != null;
        }

        public static XVaildValueAttribute GetVaild(MemberInfo member)
        {
            return member.GetCustomAttribute<XVaildValueAttribute>(false);
        }

        private static object GetValue(object instance, MemberInfo member)
        {
            var field = member as FieldInfo;
            var property = member as PropertyInfo;
            if (field != null)
                return field.GetValue(instance);
            if (property != null)
                return property.GetValue(instance);
            throw new Exception("未获取到值");
        }
        private static void ThrowNull(MemberInfo member)
        {
            throw new Exception(string.Format("{0}的值为空", member.Name));
        }

        public static void ThrowVaild(MemberInfo member)
        {
            throw new Exception(string.Format("{0}的值为未通过验证", member.Name));
        }
    }
}
